// ==UserScript==
// @name 🥇超星/学习通网课小助手|修复视频播放|自动跳转任务点|章测作业字体解密|自动答题|题库覆盖率高、完全免费
// @namespace noshuang
// @version 3.2
// @description ✨超星学习通视频、章节测试、作业小助手,目前支持以下功能⚪视频自动观看,跳转下一个任务点⚪章节测试自动完成,无答案保存⚪作业自动完成,自动保存
// @author noshuang
// @match *://*.chaoxing.com/*
// @connect cx.icodef.com
// @connect s.jiaoyu139.com
// @run-at document-end
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// @require https://lib.baomitu.com/jquery/2.0.0/jquery.min.js
// @require https://cdn.jsdelivr.net/gh/photopea/Typr.js@15aa12ffa6cf39e8788562ea4af65b42317375fb/src/Typr.min.js
// @require https://cdn.jsdelivr.net/gh/photopea/Typr.js@f4fcdeb8014edc75ab7296bd85ac9cde8cb30489/src/Typr.U.min.js
// @resource Table https://www.forestpolice.org/ttf/2.0/table.json
// @license MIT
// @original-script https://greasyfork.org/scripts/369625
// @original-author wyn665817
// @original-license MIT
// ==/UserScript==
// 设置修改后,需要刷新或重新打开网课页面才会生效(0关闭,1开启)
var setting = {
// 5E3 == 5000,科学记数法,表示毫秒数
time: 5E3 // 默认响应速度为5秒,不建议小于3秒
, review: 0 // 复习模式,完整挂机视频(音频)时长,支持挂机任务点已完成的视频和音频
, queue: 1 // 队列模式,开启后任务点逐一完成,关闭则单页面所有任务点同时进行
// 1代表开启,0代表关闭
, video: 1 // 视频支持后台、切换窗口不暂停,支持多视频
, work: 1 // 自动答题功能(章节测验),作业需要手动开启查询,高准确率
, audio: 1 // 音频自动播放,与视频功能共享vol和rate参数
, book: 1 // 图书阅读任务点,非课程阅读任务点
, docs: 1 // 文档阅读任务点,PPT类任务点自动完成阅读任务
, jump: 1 // 自动切换标签、章节、课程(需要配置course参数)
, read: '10' // 挂机课程阅读时间,单位分钟
, total: 1 // 显示课程进度的统计数据,在学习进度页面的上方展示
// 仅开启video(audio)时,修改此处才会生效
, line: '公网1' // 视频播放的默认资源线路
, http: '标清' // 视频播放的默认清晰度
, vol: '0' // 默认音量的百分数,设定范围:[0,100],'0'为静音
, rate: '0' // 倍速已经失效,建议一倍速看,多倍速会异常提示
// 仅开启work时,修改此处才会生效
, auto: 1 // 答题完成后自动提交,默认开启
, none: 0 // 无匹配答案时执行默认操作,关闭后若题目无匹配答案则会暂时保存已作答的题目
, scale: 1 // 富文本编辑器高度自动拉伸,用于文本类题目,答题框根据内容自动调整大小
, gs: 1 // 焦点跟随
// 仅开启jump时,修改此处才会生效
, course: 1 // 当前课程完成后自动切换课程,仅支持按照根目录课程顺序切换,默认关闭
, lock: 1 // 跳过未开放(图标是锁)的章节,即闯关模式或定时发放的任务点,默认开启
},
_self = unsafeWindow,
url = location.pathname,
top = _self,
api = 'http://cx.icodef.com/wyn-nb?v=4';
// 页面中转
if (url != '/studyApp/studying' && top != _self.top) document.domain = location.host.replace(/.+?\./, '');
try {
while (top != _self.top) {
top = top.parent.document ? top.parent : _self.top;
if (top.location.pathname == '/mycourse/studentstudy') break;
}
} catch (err) {
// console.log(err);
top = _self;
}
var $ = _self.jQuery || top.jQuery,
parent = _self == top ? self : _self.parent,
Ext = _self.Ext || parent.Ext || {},
UE = _self.UE,
vjs = _self.videojs;
$('.Header').find('a:contains(回到旧版)')[0] ? $('.Header').find('a:contains(回到旧版)')[0].click() : '';
String.prototype.toCDB = function () {
return this.replace(/\s/g, '').replace(/[\uff01-\uff5e]/g, function (str) {
return String.fromCharCode(str.charCodeAt(0) - 65248);
}).replace(/[“”]/g, '"').replace(/[‘’]/g, "'").replace(/。/g, '.');
};
_self.alert = console.log;
setting.normal = ''; // ':visible'
setting.job = [':not(*)'];
setting.video && setting.job.push('iframe[src*="/video/index.html"]');
setting.work && setting.job.push('iframe[src*="/work/index.html"]');
setting.audio && setting.job.push('iframe[src*="/audio/index.html"]');
setting.book && setting.job.push('iframe[src*="/innerbook/index.html"]');
setting.docs && setting.job.push('iframe[src*="/ppt/index.html"]', 'iframe[src*="/pdf/index.html"]');
if (url == '/mycourse/studentstudy') {
showTips();
_self.checkMobileBrowerLearn = $.noop;
var classId = location.search.match(/cla[zs]{2}id=(\d+)/i)[1] || 0,
courseId = _self.courseId || location.search.match(/courseId=(\d+)/i)[1] || 0;
!setting.jump || setting.lock || $('#coursetree').on('click', '[onclick*=void], [href*=void]', function () {
_self.getTeacherAjax(courseId, classId, $(this).parent().attr('id').slice(3));
});
} else if (url == '/ananas/modules/video/index.html' && setting.video) {
if (setting.review) _self.greenligth = Ext.emptyFn;
passVideo()
} else if (url == '/work/doHomeWorkNew' || url == '/api/work' || url == '/work/addStudentWorkNewWeb') {
top.courseId = location.search.match(/courseId=(\d+)/i)[1];
if (!UE) {
var len = ($ || Ext.query || Array)('font:contains(未登录)', document).length;
setTimeout(len == 1 ? top.location.reload : parent.greenligth, setting.time);
} else if (setting.work) {
setTimeout(relieveLimit, 0);
beforeFind();
}
} else if (url == '/ananas/modules/innerbook/index.html' && setting.book) {
setTimeout(function () {
if (getTip()) _self.setting ? _self.top.onchangepage(_self.getFrameAttr('end')) : _self.greenligth();
}, setting.time);
} else if (url.match(/^\/ananas\/modules\/(ppt|pdf)\/index\.html$/) && setting.docs) {
setTimeout(function () {
if (getTip()) _self.setting ? _self.finishJob() : _self.greenligth();
}, setting.time);
frameElement.setAttribute('download', 1);
if (_self.downloadNum == '') _self.downloadNum = '1';
} else if (url == '/knowledge/cards') {
$ && checkToNext();
} else if (url.match(/^\/(course|zt)\/\d+\.html$/)) {
setTimeout(function () {
+setting.read && _self.sendLogs && $('.course_section:eq(0) .chapterText').click();
}, setting.time);
} else if (url == '/ztnodedetailcontroller/visitnodedetail') {
setting.read *= 60 / $('.course_section').length;
setting.read && _self.sendLogs && autoRead();
} else if (url == '/mycourse/studentcourse') {
var gv = location.search.match(/d=\d+&/g);
setting.total && $('', {
href: '/moocAnalysis/chapterStatisticByUser?classI' + gv[1] + 'courseI' + gv[0] + 'userId=' + _self.getCookie('_uid') + '&ut=s',
target: '_blank',
title: '点击查看章节统计',
style: 'margin: 0 25px;',
html: '本课程共' + $('.icon').length + '节,剩余' + $('em:not(.openlock)').length + '节未完成'
}).appendTo('.zt_logo').parent().width('auto');
} else if (location.hostname == 'i.mooc.chaoxing.com') {
} else if (url == '/work/selectWorkQuestionYiPiYue') {
submitAnswer(getIframe().parent(), $.extend(true, [], parent._data));
} else if (url == '/exam-ans/exam/test') {
showTips();
$('.page').eq(0).html("本脚本因为已知的不可抗力原因无法使用考试功能噢,可以使用左上角图片的app尝试考试,
正规考试请勿作弊!");
}
;
function whatttf() {
var $tip = $('style:contains(font-cxsecret)');
if (!$tip.length) return;
var font = $tip.text().match(/base64,([\w\W]+?)'/)[1];
font = Typr.parse(base64ToUint8Array(font))[0];
var table = JSON.parse(GM_getResourceText('Table'));
var match = {};
for (var i = 19968; i < 40870; i++) { // 中文[19968, 40869]
$tip = Typr.U.codeToGlyph(font, i);
if (!$tip) continue;
$tip = Typr.U.glyphToPath(font, $tip);
$tip = $.md5(JSON.stringify($tip)).slice(24); // 8位即可区分
match[i] = table[$tip];
}
// 替换加密字体
$('.font-cxsecret').html(function (index, html) {
$.each(match, function (key, value) {
key = String.fromCharCode(key);
key = new RegExp(key, 'g');
value = String.fromCharCode(value);
html = html.replace(key, value);
});
return html;
}).removeClass('font-cxsecret'); // 移除字体加密
function base64ToUint8Array(base64) {
var data = window.atob(base64);
var buffer = new Uint8Array(data.length);
for (var i = 0; i < data.length; ++i) {
buffer[i] = data.charCodeAt(i);
}
return buffer;
}
}
function time_to_sec(time) {
let s = 0;
let t = 1;
for (let i = time.split(':').length - 1; i >= 0; i--) {
s += Number(time.split(':')[i]) * t
t *= 60
}
return s;
};
function getTip() {
return top != _self && jobSort($ || Ext.query);
}
function passVideo() {
// rateHack()
getTip() && setTimeout(() => {
showTips();
let vd = $('video')[0];
if (vd) {
console.log('播放视频');
vd.volume = 1;
$('.vjs-big-play-button')[0].click();
}
}, 2000)
}
function jobSort($) {
var fn = $.fn ? [getIframe(1), 'length'] : [self, 'dom'],
sel = setting.job.join(', :not(.ans-job-finished) > .ans-job-icon' + setting.normal + ' ~ ');
if (!getIframe()[fn[1]] || getIframe().parent().is('.ans-job-finished')) return 0;
if (!setting.queue || $(sel, fn[0].parent.document)[0] == fn[0].frameElement) return 1;
setInterval(function () {
$(sel, fn[0].parent.document)[0] == fn[0].frameElement && fn[0].location.reload();
}, setting.time);
}
function getIframe(tip, win, job) {
if (!$) return Ext.get(frameElement || []).parent().child('.ans-job-icon') || Ext.get([]);
do {
win = win ? win.parent : _self;
job = $(win.frameElement).prevAll('.ans-job-icon');
} while (!job.length && win.parent.frameElement);
return tip ? win : job;
}
function relieveLimit() {
if (setting.scale) _self.UEDITOR_CONFIG.scaleEnabled = false;
$.each(UE.instants, function () {
var key = this.key;
this.ready(function () {
this.destroy();
UE.getEditor(key);
});
});
}
function showTips() {
setting.div = _self.top.$(
'